package handler

import (
	"encoding/json"
	"errors"
	"fmt"
	"log"
	"net/http"

	"bbs/pkg/domain"
	"bbs/pkg/model/helper"
	"bbs/pkg/model/session"
)

type userHandler struct {
	userSvc domain.UserService
}

func NewUserHandler(userSvc domain.UserService) domain.UserHandler {
	return &userHandler{
		userSvc: userSvc,
	}
}

func (h *userHandler) Register(w http.ResponseWriter, r *http.Request) {
	registerUser := &domain.ResiterUser{}
	if err := json.NewDecoder(r.Body).Decode(registerUser); err != nil {
		helper.ResponseErrJSON(w, http.StatusBadRequest, err)
		return
	}

	// check user fileds
	if err := h.userSvc.CheckUser(registerUser); err != nil {
		helper.ResponseErrJSON(w, http.StatusBadRequest, fmt.Errorf("your message error: %v", err))
		return
	}

	// create user
	if err := h.userSvc.CreateUser(registerUser); err != nil {
		helper.ResponseText(w, http.StatusInternalServerError, "")
		log.Println("register user failed: ", err)
		return
	}

	d := map[string]interface{}{
		"user": struct {
			Name  string `json:"name"`
			Email string `json:"email"`
		}{
			Name:  registerUser.Name,
			Email: registerUser.Email,
		},
	}
	helper.ResponseJSON(w, http.StatusOK, d)
}

func (h *userHandler) Login(w http.ResponseWriter, r *http.Request) {
	payload := &domain.LoginUser{}
	if err := json.NewDecoder(r.Body).Decode(payload); err != nil {
		helper.ResponseErrJSON(w, http.StatusBadRequest, fmt.Errorf("username or password error, %v", err))
		return
	}

	user, err := h.userSvc.AuthenticateUser(payload)
	if err != nil {
		helper.ResponseText(w, http.StatusInternalServerError, "")
		log.Println("authenticate user failed, ", err)
		return
	}

	if err := h.buildSession(w, r, user); err != nil {
		helper.ResponseText(w, http.StatusInternalServerError, "")
		log.Println("get session failed, ", err)
		return
	}

	d := map[string]interface{}{
		"user": struct {
			ID    string `json:"id"`
			Name  string `json:"name"`
			Email string `json:"email"`
		}{
			ID:    user.ID,
			Name:  user.Name,
			Email: user.Email,
		},
	}
	helper.ResponseJSON(w, http.StatusOK, d)
}

func (h *userHandler) Logout(w http.ResponseWriter, r *http.Request) {
	sess, err := session.GetSession(r)
	if err != nil {
		helper.ResponseText(w, http.StatusUnauthorized, "")
		log.Println("get session failed, ", err)
		return
	}

	for k := range sess.Values {
		delete(sess.Values, k)
	}
	sess.Options.MaxAge = -1
	if err := sess.Save(r, w); err != nil {
		helper.ResponseText(w, http.StatusInternalServerError, "")
		log.Println("clean session failed, ", err)
		return
	}

	helper.ResponseText(w, http.StatusNoContent, "")
}

func (h *userHandler) GetSettings(w http.ResponseWriter, r *http.Request) {
	sess, err := session.GetSession(r)
	if err != nil {
		helper.ResponseErrJSON(w, http.StatusUnauthorized, fmt.Errorf("please login first"))
		return
	}
	userID, ok := sess.Values["user_id"].(string)
	if !ok {
		helper.ResponseText(w, http.StatusUnauthorized, "")
		return
	}

	user, err := h.userSvc.GetUserByID(userID)
	if err != nil {
		helper.ResponseText(w, http.StatusInternalServerError, "")
		return
	}

	d := map[string]interface{}{
		"user": user,
	}
	helper.ResponseJSON(w, http.StatusOK, d)
}

func (h *userHandler) UpdateSettings(w http.ResponseWriter, r *http.Request) {
	sess, err := session.GetSession(r)
	if err != nil {
		helper.ResponseText(w, http.StatusUnauthorized, "")
		return
	}
	userID, ok := sess.Values["user_id"].(string)
	if !ok {
		helper.ResponseText(w, http.StatusUnauthorized, "")
		return
	}

	payload := &domain.Settings{}
	if err := json.NewDecoder(r.Body).Decode(payload); err != nil {
		helper.ResponseErrJSON(w, http.StatusBadRequest, fmt.Errorf("message incorted, %v", err))
		return
	}
	user, err := h.userSvc.GetUserByID(userID)
	if err != nil {
		helper.ResponseText(w, http.StatusInternalServerError, "")
		log.Println("get user by id failed, ", err)
		return
	}
	user.Settings = *payload
	if err := h.userSvc.UpdateUser(user); err != nil {
		helper.ResponseText(w, http.StatusInternalServerError, "")
		log.Println("update settings failed, ", err)
		return
	}

	d := map[string]interface{}{
		"user": user,
	}
	helper.ResponseJSON(w, http.StatusOK, d)
}

func (h *userHandler) SendCaptcha(w http.ResponseWriter, r *http.Request) {
	payload := &struct {
		Email string `json:"email"`
	}{}
	if err := json.NewDecoder(r.Body).Decode(payload); err != nil {
		helper.ResponseErrJSON(w, http.StatusBadRequest, fmt.Errorf("request error, %v", err))
		return
	}

	if _, err := h.userSvc.GetUserByEmail(payload.Email); err != nil {
		helper.ResponseErrJSON(w, http.StatusBadRequest, fmt.Errorf("email error, %v", err))
		return
	}

	if err := h.userSvc.Send(payload.Email); err != nil {
		helper.ResponseText(w, http.StatusInternalServerError, "")
		log.Println("send email failed, ", err)
		return
	}

	helper.ResponseText(w, http.StatusNoContent, "")
}

func (h *userHandler) ResetPassword(w http.ResponseWriter, r *http.Request) {
	payload := &domain.Reseting{}
	if err := json.NewDecoder(r.Body).Decode(payload); err != nil {
		helper.ResponseErrJSON(w, http.StatusBadRequest, fmt.Errorf("requset error, %v", err))
		return
	}
	if payload.New_Password != payload.RePassword {
		helper.ResponseErrJSON(w, http.StatusBadRequest, errors.New("the two password do not match"))
		return
	}

	if err := h.userSvc.ResetPassword(payload); err != nil {
		helper.ResponseText(w, http.StatusInternalServerError, "")
		log.Println("reset password failed, ", err)
		return
	}

	helper.ResponseText(w, http.StatusOK, "Reset Password Successfully")
}

func (h *userHandler) buildSession(w http.ResponseWriter, r *http.Request, user *domain.User) error {
	sess, err := session.GetSession(r)
	if err != nil {
		return err
	}
	sess.Values["user_id"] = user.ID
	sess.Values["user_email"] = user.Email
	sess.Values["user_name"] = user.Name
	sess.Values["user_create_at"] = user.Created
	sess.Values["user_update_at"] = user.Updated
	if err := sess.Save(r, w); err != nil {
		return err
	}
	return nil
}
